package com.md.util;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.google.common.collect.Maps;
import com.md.base.exception.ServerException;
import com.md.strategy.impl.format.BigDecimalFormatter;
import com.md.strategy.impl.format.DateFormatter;
import com.md.strategy.impl.format.LocalDateTimeFormatter;
import com.md.value.constants.Constants;
import com.md.value.constants.MatchConstants;
import com.md.value.enums.ErrorEnum;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Matcher;

import static java.time.format.DateTimeFormatter.ofPattern;

/**
 * 字符串工具类
 * 2021-09-17 23:24
 *
 * @author 刘骄阳
 */
@UtilityClass
@Slf4j
public class StringUtil {
  private static final String LOCAL_TIME_PATTERN = "HH:mm:ss";
  private static final String LOCAL_DATE_PATTERN = "yyyy-MM-dd";
  private static final String LOCAL_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

  /**
   * jackSon序列化json
   */
  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
  /**
   * jackSon序列化xml
   */
  private static final XmlMapper XML_MAPPER = new XmlMapper();
  private static final Map<Class<?>, Function<Object, String>> FORMAT = Maps.newHashMap();

  static {
    // 序列化
    buildObjectMapper();
    // 格式化
    buildFormat();
  }

  private void buildObjectMapper() {
    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(ofPattern(LOCAL_TIME_PATTERN)));
    javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(ofPattern(LOCAL_TIME_PATTERN)));
    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(ofPattern(LOCAL_DATE_PATTERN)));
    javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(ofPattern(LOCAL_DATE_PATTERN)));
    javaTimeModule.addSerializer(LocalDateTime.class,
      new LocalDateTimeSerializer(ofPattern(LOCAL_DATE_TIME_PATTERN)));
    javaTimeModule.addDeserializer(LocalDateTime.class,
      new LocalDateTimeDeserializer(ofPattern(LOCAL_DATE_TIME_PATTERN)));
    OBJECT_MAPPER.registerModule(javaTimeModule);
    OBJECT_MAPPER.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
    OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
  }

  private void buildFormat() {
    FORMAT.clear();
    FORMAT.put(BigDecimal.class, x -> BigDecimalFormatter.INSTANCE.format((BigDecimal) x));
    FORMAT.put(LocalDateTime.class, x -> LocalDateTimeFormatter.INSTANCE.format((LocalDateTime) x));
    FORMAT.put(Date.class, x -> DateFormatter.INSTANCE.format((Date) x));
  }

  /**
   * 颠倒字符串
   *
   * @param value 要颠倒的字符串
   * @return 颠倒后的字符串
   */
  public static String invert(final String value) {
    int length = value.length();
    StringBuilder sb = new StringBuilder(length);
    for (int i = length - 1; i >= 0; i--) {
      sb.append(value.charAt(i));
    }
    return sb.toString();
  }

  /**
   * 将驼峰命名的字符串变为下划线
   *
   * @param str 驼峰字符串
   * @return 带下划线的字符串
   */
  public static String humpToLine(final String str) {
    Matcher matcher = MatchConstants.HUMP_PATTERN.matcher(str);
    StringBuffer sb = new StringBuffer(str.length());
    while (matcher.find()) {
      matcher.appendReplacement(sb, Constants.UNDERLINE + matcher.group(0).toLowerCase());
    }
    matcher.appendTail(sb);
    return sb.toString();
  }

  /**
   * 将带有下划线的字符串变为驼峰
   *
   * @param str 带下划线的字符串
   * @return 驼峰字符串
   */
  public static String lineToHump(final String str) {
    Matcher matcher = MatchConstants.LINE_PATTERN.matcher(str);
    StringBuffer sb = new StringBuffer(str.length());
    ArrayList<Integer> list = new ArrayList<>();

    while (matcher.find()) {
      matcher.appendReplacement(sb, Constants.EMPTY);
      list.add(sb.length());
    }
    matcher.appendTail(sb);
    for (Integer index : list) {
      sb.replace(index, index + 1, sb.substring(index,
        index + 1).toUpperCase());
    }
    return sb.toString();
  }

  /**
   * 将实体类转换为json字符串
   *
   * @param object 实体类
   * @return json字符串
   */
  public static String objectToJson(final Object object) {
    try {
      return OBJECT_MAPPER.writeValueAsString(object);
    } catch (JsonProcessingException e) {
      log.error("序列化异常: {}", e.getMessage(), e);
      throw ServerException.build(ErrorEnum.SERIALIZE_ERROR);
    }
  }

  /**
   * 将实体类转换为json字符串
   *
   * @param object 实体类
   * @return json字符串
   */
  public static String objectToXML(final Object object) {
    try {
      return XML_MAPPER.writeValueAsString(object);
    } catch (JsonProcessingException e) {
      log.error("序列化异常: {}", e.getMessage(), e);
      throw ServerException.build(ErrorEnum.SERIALIZE_ERROR);
    }
  }

  /**
   * 将json字符串转为map集合
   *
   * @param json  json字符串
   * @param clazz map集合类型
   * @param <T>   类型
   * @return map集合
   */
  public static <T, E> Map<T, E> jsonToMap(final String json, final TypeReference<Map<T, E>> clazz) {
    try {
      return OBJECT_MAPPER.readValue(json, clazz);
    } catch (JsonProcessingException e) {
      log.error("序列化异常: {}", e.getMessage(), e);
      throw ServerException.build(ErrorEnum.SERIALIZE_ERROR);
    }
  }

  /**
   * 将json字符串转为map集合
   *
   * @param json json字符串
   * @return map集合
   */
  public static <K, V> Map<K, V> jsonToMap(final String json) {
    try {
      if (ObjectUtil.isEmpty(json)) {
        return Maps.newHashMap();
      }
      return OBJECT_MAPPER.readValue(json, new TypeReference<Map<K, V>>() {
      });
    } catch (JsonProcessingException e) {
      log.error("序列化异常: {}", e.getMessage(), e);
      throw ServerException.build(ErrorEnum.SERIALIZE_ERROR);
    }
  }

  public static <T> T jsonToObject(final String json, final TypeReference<T> clazz) {
    try {
      if (ObjectUtil.isEmpty(json)) {
        return null;
      }
      return OBJECT_MAPPER.readValue(json, clazz);
    } catch (JsonProcessingException e) {
      log.error("序列化异常: {}", e.getMessage(), e);
      throw ServerException.build(ErrorEnum.SERIALIZE_ERROR);
    }
  }

  /**
   * 将json字符串转为实体类
   *
   * @param json  json字符串
   * @param clazz 实体类类型
   * @param <T>   类型
   * @return 实体类
   */
  public static <T> T jsonToObject(final String json, final Class<T> clazz) {
    try {
      if (ObjectUtil.isEmpty(json)) {
        return null;
      }
      return OBJECT_MAPPER.readValue(json, clazz);
    } catch (JsonProcessingException e) {
      log.error("序列化异常: {}", e.getMessage(), e);
      throw ServerException.build(ErrorEnum.SERIALIZE_ERROR);
    }
  }

  public static String maskString(final String value) {
    return maskString(value, 1);
  }

  public static String maskString(final String value, final Integer length) {
    if (ObjectUtil.isEmpty(value) || value.length() <= (length * 2)) {
      // 如果字符串的长度小于等于n*2，则不做处理
      return value;
    }

    // 获取首字符和尾字符
    String firstChar = value.substring(0, length);
    String lastChar = value.substring(value.length() - length);

    String middle = value.substring(length, value.length() - length);

    // 传统方法：通过 StringBuilder 来生成星号字符串
    StringBuilder maskedMiddle = new StringBuilder();
    int middleLength = middle.length();
    for (int i = 0; i < middleLength; i++) {
      maskedMiddle.append("*");
    }

    // 拼接成新的字符串
    return firstChar + maskedMiddle + lastChar;
  }

  /**
   * 获取字符串
   *
   * @param value 字符串
   * @return 字符串
   */
  public static String getToString(final Object value) {
    if (ObjectUtil.isEmpty(value)) {
      return "";
    }
    Optional<String> formatted = FORMAT.entrySet().stream().filter(entry -> entry.getKey().isInstance(value))
      .map(entry -> entry.getValue().apply(value)).findFirst();
    return formatted.orElseGet(() -> String.valueOf(value));
  }

  /**
   * 删除空格
   *
   * @param value 字符串
   * @return 字符串
   */
  public static String trim(final String value) {
    if (ObjectUtil.isEmpty(value)) {
      return "";
    }
    return value.trim();
  }

}
